package client

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"gitee.com/zh-jn/harmonica-previewer-online/api"
)

type Config struct {
	Addr string
	Port int
}

type Rsp struct {
	Code int    `json:"code"`
	Data string `json:"data"`
	Msg  string `json:"msg"`
}

func NewLocalFileStorageClient(addr string, port int, prefix string) api.Storage {
	return &LocalFileStorageClient{
		cfg: Config{
			Addr: addr,
			Port: port,
		},
		keyPrefix: prefix,
		httpc:     &http.Client{},
	}
}

type LocalFileStorageClient struct {
	cfg       Config
	httpc     *http.Client
	keyPrefix string
}

func (c *LocalFileStorageClient) getAddr() string {
	return fmt.Sprintf("%v:%v", c.cfg.Addr, c.cfg.Port)
}

func (c *LocalFileStorageClient) getKey(key api.StorageKey) api.StorageKey {
	return api.StorageKey(fmt.Sprintf("%v_%v", c.keyPrefix, key))
}

func (c *LocalFileStorageClient) Get(key api.StorageKey) (io.Reader, error) {
	key = c.getKey(key)
	rsp, err := c.httpc.Get(fmt.Sprintf("http://%v/get/%v", c.getAddr(), key))
	if err != nil {
		return nil, err
	}

	bodybuf, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		return nil, err
	}

	var r Rsp
	if err = json.Unmarshal(bodybuf, &r); err != nil {
		return nil, err
	}

	if r.Code != 0 {
		return nil, fmt.Errorf("remote err, code = %v, msg = %v", r.Code, r.Msg)
	}

	databuf, err := base64.StdEncoding.DecodeString(r.Data)
	if err != nil {
		return nil, fmt.Errorf("decode base64 err, %v", err)
	}

	return bytes.NewReader(databuf), nil
}

func (c *LocalFileStorageClient) Put(key api.StorageKey, reader io.Reader) error {
	key = c.getKey(key)
	rsp, err := c.httpc.Post(fmt.Sprintf("http://%v/put/%v", c.getAddr(), key), "", reader)
	if err != nil {
		return err
	}

	if rsp.StatusCode != http.StatusOK {
		bodybuf, err := ioutil.ReadAll(rsp.Body)
		if err != nil {
			return fmt.Errorf("rsp status code = %v", rsp.StatusCode)
		}
		return fmt.Errorf("rsp status code = %v, body = %v", rsp.StatusCode, string(bodybuf))
	}
	bodybuf, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		return fmt.Errorf("read rsp body err, %v", err)
	}
	var r Rsp
	if err = json.Unmarshal(bodybuf, &r); err != nil {
		return err
	}
	if r.Code != 0 {
		return fmt.Errorf("remote err, code = %v, msg = %v", r.Code, r.Msg)
	}
	return nil
}

func (c *LocalFileStorageClient) Exists(key api.StorageKey) (bool, error) {
	key = c.getKey(key)
	rsp, err := c.httpc.Get(fmt.Sprintf("http://%v/exists/%v", c.getAddr(), key))
	if err != nil {
		return false, err
	}
	bodybuf, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		return false, fmt.Errorf("read rsp body err, %v", err)
	}
	var ret bool
	if err = api.ReadRsp(bodybuf, &ret); err != nil {
		return false, err
	}

	return ret, nil
}
